package com.project.common.core.utils.Ali;

import com.alipay.api.AlipayApiException;
import com.alipay.api.AlipayClient;
import com.alipay.api.DefaultAlipayClient;
import com.alipay.api.domain.AlipayTradeAppPayModel;
import com.alipay.api.request.*;
import com.alipay.api.response.AlipayFundTransOrderQueryResponse;
import com.alipay.api.response.AlipayFundTransToaccountTransferResponse;
import com.alipay.api.response.AlipayTradeAppPayResponse;
import com.project.common.core.config.prop.PropAttributes;
import com.project.common.core.config.prop.PropConfig;
import com.project.common.core.utils.JacksonUtil;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.math.BigDecimal;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

/**
 * 阿里支付工具类
 *
 * @author WYY
 * @date 2017/12/6
 */
public class AliPayUtil {
    /**
     * 日志工具类
     */
    public static Logger log = LoggerFactory.getLogger(AliPayUtil.class);

    /**
     * 支付宝支付网关、签名类型
     */
    public static final String ALI_PAY_GATEWAY = PropConfig.getProperty(PropAttributes.THIRDPARTY_ALI_PAY_GATEWAY);
    public static final String ALI_PAY_SIGN_TYPE = PropConfig.getProperty(PropAttributes.THIRDPARTY_ALI_PAY_SIGN_TYPE);

    /**
     * PC网站支付--商家后台
     */
    public static final String ALI_APP_ID_PAGE_BIZ = PropConfig.getProperty(PropAttributes.THIRDPARTY_ALI_APP_ID_PAGE_BIZ);
    public static final String ALI_APP_PRIVATE_KEY_PAGE_BIZ = PropConfig.getProperty(PropAttributes.THIRDPARTY_ALI_APP_PRIVATE_KEY_PAGE_BIZ);
    public static final String ALI_PAY_PUBLIC_KEY_PAGE_BIZ = PropConfig.getProperty(PropAttributes.THIRDPARTY_ALI_PAY_PUBLIC_KEY_PAGE_BIZ);
    public static final String THIRDPARTY_ALI_PAY_TRADE_PAGE_BIZ_DOMAIN = PropConfig.getProperty(PropAttributes.THIRDPARTY_ALI_PAY_TRADE_PAGE_BIZ_DOMAIN);

    /**
     * PC网站支付--网站
     */
    public static final String ALI_APP_ID_PAGE_SITE = PropConfig.getProperty(PropAttributes.THIRDPARTY_ALI_APP_ID_PAGE_SITE);
    public static final String ALI_APP_PRIVATE_KEY_PAGE_SITE = PropConfig.getProperty(PropAttributes.THIRDPARTY_ALI_APP_PRIVATE_KEY_PAGE_SITE);
    public static final String ALI_PAY_PUBLIC_KEY_PAGE_SITE = PropConfig.getProperty(PropAttributes.THIRDPARTY_ALI_PAY_PUBLIC_KEY_PAGE_SITE);
    public static final String THIRDPARTY_ALI_PAY_TRADE_PAGE_SITE_DOMAIN = PropConfig.getProperty(PropAttributes.THIRDPARTY_ALI_PAY_TRADE_PAGE_SITE_DOMAIN);

    /**
     * 手机移动端支付
     */
    public static final String ALI_APP_ID_WAP = PropConfig.getProperty(PropAttributes.THIRDPARTY_ALI_APP_ID_WAP);
    public static final String ALI_APP_PRIVATE_KEY_WAP = PropConfig.getProperty(PropAttributes.THIRDPARTY_ALI_APP_PRIVATE_KEY_WAP);
    public static final String ALI_PAY_PUBLIC_KEY_WAP = PropConfig.getProperty(PropAttributes.THIRDPARTY_ALI_PAY_PUBLIC_KEY_WAP);
    public static final String THIRDPARTY_ALI_PAY_TRADE_WAP_DOMAIN = PropConfig.getProperty(PropAttributes.THIRDPARTY_ALI_PAY_TRADE_WAP_DOMAIN);

    /**
     * 转账到支付宝账户
     */
    public static final String ALI_APP_ID_TRANS_ADMIN = PropConfig.getProperty(PropAttributes.THIRDPARTY_ALI_APP_ID_TRANS_ADMIN);
    public static final String ALI_APP_PRIVATE_KEY_TRANS_ADMIN = PropConfig.getProperty(PropAttributes.THIRDPARTY_ALI_APP_PRIVATE_KEY_TRANS_ADMIN);
    public static final String ALI_PAY_PUBLIC_KEY_TRANS_ADMIN = PropConfig.getProperty(PropAttributes.THIRDPARTY_ALI_PAY_PUBLIC_KEY_TRANS_ADMIN);

    /**
     * app支付
     */
    public static final String ALI_PAY_TIME_OUT_EXPRESS = PropConfig.getProperty(PropAttributes.THIRDPARTY_ALI_PAY_TIME_OUT_EXPRESS);

    /**
     * AliPay APP支付
     */
    public static final String ALI_APP_ID_APP = PropConfig.getProperty(PropAttributes.THIRDPARTY_ALI_APP_ID_APP);
    public static final String ALI_APP_PRIVATE_KEY_APP = PropConfig.getProperty(PropAttributes.THIRDPARTY_ALI_APP_PRIVATE_KEY_APP);
    public static final String ALI_PAY_PUBLIC_KEY_APP = PropConfig.getProperty(PropAttributes.THIRDPARTY_ALI_PAY_PUBLIC_KEY_APP);
    public static final String ALI_PAY_PUBLIC_KEY = PropConfig.getProperty(PropAttributes.THIRDPARTY_ALI_PAY_PUBLIC_KEY);

    /**
     * 支付宝账户类型枚举
     * 1:2088开头的16位数字
     * 2:手机号或Email
     */
    public enum PAYEE_TYPE_ENUM {
        ALI_PAY_ACCT_ID(1, "支付宝唯一用户号", "ALIPAY_USERID"),
        ALI_PAY_LOGIN_NO(2, "支付宝登录号", "ALIPAY_LOGONID");

        /**
         * 枚举值
         */
        private Integer value;

        /**
         * 枚举名称
         */
        private String name;

        /**
         * 枚举编码
         */
        private String code;

        /**
         * 枚举有参构造函数
         *
         * @param value 枚举值
         * @param name  枚举名
         * @param code  枚举编码
         */
        PAYEE_TYPE_ENUM(Integer value, String name, String code) {
            this.value = value;
            this.name = name;
            this.code = code;
        }

        /**
         * 根据枚举值获取枚举对象
         *
         * @param value 枚举值
         */
        public static AliPayUtil.PAYEE_TYPE_ENUM getEnum(Integer value) {
            for (AliPayUtil.PAYEE_TYPE_ENUM payeeTypeEnum : AliPayUtil.PAYEE_TYPE_ENUM.values()) {
                if (value.equals(payeeTypeEnum.getValue())) {
                    return payeeTypeEnum;
                }
            }
            return null;
        }

        /**
         * 获取枚举值
         */
        public Integer getValue() {
            return value;
        }

        /**
         * 设置枚举值
         */
        public void setValue(Integer value) {
            this.value = value;
        }

        /**
         * 获取枚举名
         */
        public String getName() {
            return name;
        }

        /**
         * 设置枚举值
         */
        public void setName(String name) {
            this.name = name;
        }

        /**
         * 获取枚举编码
         */
        public String getCode() {
            return code;
        }

        /**
         * 设置枚举编码
         */
        public void setCode(String code) {
            this.code = code;
        }
    }

    /**
     * 阿里网站网页支付【page支付】
     *
     * @param orderNo    商户订单号
     * @param totalAmt   支付总金额【单位为元；且保留两位小数】
     * @param aliPayPojo 阿里支付对象
     * @param request    Request请求对象
     * @param response   Response响应对象
     * @throws Exception
     */
    public static void alipayTradePagepay(String orderNo, BigDecimal totalAmt, AliPayPojo aliPayPojo, HttpServletRequest request, HttpServletResponse response) throws Exception {
        // 初始化阿里支付客户端
        AlipayClient alipayClient = new DefaultAlipayClient(ALI_PAY_GATEWAY, aliPayPojo.getAliAppId(), aliPayPojo.getAliAppPrivateKey(),
                "JSON", "utf-8", aliPayPojo.aliPayPublicKey, ALI_PAY_SIGN_TYPE);

        // 初始化请求对象
        AlipayTradePagePayRequest aliPayReq = new AlipayTradePagePayRequest();
        aliPayReq.setReturnUrl(aliPayPojo.getReturnUrl());
        aliPayReq.setNotifyUrl(aliPayPojo.getAsyNotifyUrl());
        Map<String, Object> bizContentMap = new HashMap<>();
        bizContentMap.put("out_trade_no", orderNo);
        bizContentMap.put("product_code", "FAST_INSTANT_TRADE_PAY");
        bizContentMap.put("total_amount", totalAmt);
        bizContentMap.put("subject", aliPayPojo.getSubject());
        bizContentMap.put("passback_params", aliPayPojo.getPassbackParams());
        bizContentMap.put("qr_pay_mode", 2);
        aliPayReq.setBizContent(JacksonUtil.toJsonStr(bizContentMap));
        String form = "";
        try {
            form = alipayClient.pageExecute(aliPayReq).getBody();
        } catch (Exception e) {
            log.error("阿里电脑网站支付API调用出错：{}", ExceptionUtils.getStackTrace(e));
        }
        response.setContentType("text/html;charset=utf-8");
        response.getWriter().write(form);
        response.getWriter().flush();
        response.getWriter().close();
    }

    /**
     * 阿里移动端H5网页支付【wap支付】
     *
     * @param orderNo  商户订单号
     * @param totalAmt 支付总金额【单位为元；且保留两位小数】
     * @param request  Request请求对象
     * @param response Response响应对象
     * @throws Exception
     */
    public static void alipayTradeWappay(String orderNo, BigDecimal totalAmt, AliPayPojo aliPayPojo, HttpServletRequest request, HttpServletResponse response) throws Exception {

        // 初始化wap支付阿里客户端
        AlipayClient alipayClient = new DefaultAlipayClient(ALI_PAY_GATEWAY, aliPayPojo.getAliAppId(), aliPayPojo.getAliAppPrivateKey(),
                "json", "UTF-8", aliPayPojo.getAliPayPublicKey(), ALI_PAY_SIGN_TYPE);

        // 封装wap支付请求参数
        AlipayTradeWapPayRequest aliReq = new AlipayTradeWapPayRequest();
        aliReq.setReturnUrl(aliPayPojo.getReturnUrl());
        aliReq.setNotifyUrl(aliPayPojo.getAsyNotifyUrl());
        Map<String, Object> bizContentMap = new HashMap<>();
        bizContentMap.put("subject", aliPayPojo.getSubject());
        bizContentMap.put("out_trade_no", orderNo);
        bizContentMap.put("total_amount", totalAmt);
        bizContentMap.put("product_code", "QUICK_WAP_WAY");
        bizContentMap.put("passback_params", aliPayPojo.getPassbackParams());
        aliReq.setBizContent(JacksonUtil.toJsonStr(bizContentMap));
        String form = "";
        try {
            form = alipayClient.pageExecute(aliReq).getBody();
        } catch (Exception e) {
            log.error("阿里移动网页支付API调用出错：{}", ExceptionUtils.getStackTrace(e));
        }

        // 相应请求表单
        response.setContentType("text/html;charset=utf-8");
        response.getWriter().write(form);
        response.getWriter().flush();
        response.getWriter().close();
    }

    /**
     * 阿里支付：单笔转账到支付宝账户
     *
     * @param orderNo      商户订单号
     * @param totalAmt     支付总金额【单位为元；且保留两位小数】
     * @param payeeType    收款账户类型【1:2088开头16位纯数字  2:邮箱或手机号】
     * @param payeeAccount 收款账户
     * @param aliPayPojo   阿里支付对象
     * @return {flag、out_biz_no、order_id、pay_date}
     * @throws Exception
     */
    public static Map<String, String> alipayFundTransToaccountTransfer(String orderNo, Integer payeeType, String payeeAccount, BigDecimal totalAmt, AliPayPojo aliPayPojo) throws Exception {

        // 初始化阿里支付客户端
        AlipayClient alipayClient = new DefaultAlipayClient(ALI_PAY_GATEWAY, aliPayPojo.getAliAppId(), aliPayPojo.getAliAppPrivateKey(),
                "json", "UTF-8", aliPayPojo.getAliPayPublicKey(), ALI_PAY_SIGN_TYPE);

        // 初始化Request请求对象
        AlipayFundTransToaccountTransferRequest request = new AlipayFundTransToaccountTransferRequest();
        Map<String, Object> bizContentMap = new HashMap<>();
        bizContentMap.put("out_biz_no", orderNo);
        bizContentMap.put("payee_type", AliPayUtil.PAYEE_TYPE_ENUM.getEnum(payeeType).getCode());
        bizContentMap.put("payee_account", payeeAccount);
        bizContentMap.put("amount", totalAmt);
        bizContentMap.put("remark", aliPayPojo.getRemark());
        request.setBizContent(JacksonUtil.toJsonStr(bizContentMap));
        AlipayFundTransToaccountTransferResponse response = alipayClient.execute(request);
        log.info("\r\n **********【单笔转账到支付宝账户】API响应：{}", JacksonUtil.toJsonStr(response));

        // 获取返回参数
        Map<String, String> returnMap = new HashMap<>();
        String code = response.getCode();
        String msg = response.getMsg();
        String subCode = response.getSubCode();
        String subMsg = response.getSubMsg();

        // 调用成功
        if (response.isSuccess()) {
            returnMap.put("flag", Boolean.TRUE.toString());
            returnMap.put("orderId", response.getOrderId());
            returnMap.put("payDate", response.getPayDate());
            return returnMap;
        }

        /**
         * 如果发生掉单、或code=20000时、或返回结果code=40004，sub_code= SYSTEM_ERROR时，
         *发起查询，如果未查询到结果，请保持原请求不变再次发起转账
         */
        Boolean queryFlag = (StringUtils.equals(code, "40004") && StringUtils.equals(subCode, "SYSTEM_ERROR"));
        if (StringUtils.equals(code, "20000") || queryFlag) {
            // 调用转账订单查询接口
            Map<String, String> queryMap = alipayFundTransOrderQuery(orderNo, null, aliPayPojo);
            String errorCode = queryMap.get("error_code");
            if (StringUtils.isNotBlank(errorCode) && StringUtils.equals(errorCode, "ORDER_NOT_EXIST")) {
                // 如果订单不存在，再次拉起转账接口
                response = alipayClient.execute(request);
                returnMap = response.getParams();
                if (response.isSuccess()) {
                    returnMap.put("orderId", response.getOrderId());
                    returnMap.put("payDate", response.getPayDate());
                    returnMap.put("flag", Boolean.TRUE.toString());
                    return returnMap;
                }
            }
        }

        returnMap.put("flag", Boolean.FALSE.toString());
        return returnMap;
    }

    /**
     * 阿里支付：查询转账支付订单
     *
     * @param outBizNo 商户订单号
     * @param orderId  支付宝转账单据号
     * @return {code、msg、order_id、status、pay_date、arrival_time_end、order_fee、fail_reason、out_biz_no、error_code}
     * @throws Exception
     */
    public static Map<String, String> alipayFundTransOrderQuery(String outBizNo, String orderId, AliPayPojo aliPayPojo) throws Exception {

        // 初始化阿里支付客户端
        AlipayClient alipayClient = new DefaultAlipayClient(ALI_PAY_GATEWAY, aliPayPojo.getAliAppId(), aliPayPojo.getAliAppPrivateKey(),
                "json", "UTF-8", aliPayPojo.getAliPayPublicKey(), ALI_PAY_SIGN_TYPE);

        // 初始化Request请求对象
        AlipayFundTransOrderQueryRequest request = new AlipayFundTransOrderQueryRequest();
        Map<String, Object> bizContentMap = new HashMap<>();
        if (StringUtils.isNotBlank(outBizNo)) {
            bizContentMap.put("out_biz_no", outBizNo);
        }
        if (StringUtils.isNotBlank(orderId)) {
            bizContentMap.put("order_id", orderId);
        }
        request.setBizContent(JacksonUtil.toJsonStr(bizContentMap));
        AlipayFundTransOrderQueryResponse response = alipayClient.execute(request);

        // 获取返回参数
        Map<String, String> returnMap = response.getParams();
        log.info("阿里支付：转账订单查询结果:{}", JacksonUtil.toJsonStr(returnMap));
        return returnMap;
    }

    /**
     * 根据Request请求返回POST请求参数Map集合
     *
     * @param request Request请求对象
     * @return 请求参数Map集合
     */
    public static Map<String, String> getParams(HttpServletRequest request) {
        // 初始化参数返回集合
        Map<String, String> params = new HashMap<>();
        Map<String, String[]> requestParams = request.getParameterMap();
        for (Iterator<String> iter = requestParams.keySet().iterator(); iter.hasNext(); ) {
            String name = (String) iter.next();
            String[] values = (String[]) requestParams.get(name);
            String valueStr = "";
            for (int i = 0; i < values.length; i++) {
                valueStr = (i == values.length - 1) ? valueStr + values[i]
                        : valueStr + values[i] + ",";
            }
            params.put(name, valueStr);
        }

        // 返回参数集合
        return params;
    }

    /**
     * @param body        对一笔交易的具体描述信息。如果是多种商品，请将商品描述字符串累加传给body。 可选
     * @param outTradeNo  商户网站唯一订单号
     * @param totalAmount 总金额
     * @param productCode 销售产品码
     * @return
     */
    public static Map<String, Object> alipayApp(String body, String outTradeNo, String productCode, String totalAmount, AliPayPojo aliPayPojo) {

        //实例化客户端
        AlipayClient alipayClient = new DefaultAlipayClient(ALI_PAY_GATEWAY, aliPayPojo.getAliAppId(), aliPayPojo.getAliAppPrivateKey(),
                "json", "UTF-8", aliPayPojo.getAliPayPublicKey(), ALI_PAY_SIGN_TYPE);
        //实例化具体API对应的request类,类名称和接口名称对应,当前调用接口名称：alipay.trade.app.pay
        AlipayTradeAppPayRequest request = new AlipayTradeAppPayRequest();
        //SDK已经封装掉了公共参数，这里只需要传入业务参数。以下方法为sdk的model入参方式(model和biz_content同时存在的情况下取biz_content)。
        AlipayTradeAppPayModel model = new AlipayTradeAppPayModel();
        model.setBody(body);
        model.setSubject(aliPayPojo.getSubject());
        model.setOutTradeNo(outTradeNo);
        model.setTimeoutExpress(ALI_PAY_TIME_OUT_EXPRESS);
        model.setTotalAmount(totalAmount);
        model.setProductCode(productCode);
        model.setPassbackParams(aliPayPojo.getPassbackParams());
        request.setBizModel(model);
        request.setNotifyUrl(aliPayPojo.getAsyNotifyUrl());
        try {
            //这里和普通的接口调用不同，使用的是sdkExecute
            AlipayTradeAppPayResponse response = alipayClient.sdkExecute(request);
            if (response.isSuccess()) {
                Map<String, Object> map = new HashMap<>();
                map.put("body", response.getBody());
                return map;
            }
        } catch (AlipayApiException e) {
            e.printStackTrace();
        }

        return null;

    }
}
